﻿/**
 * @file ghostPredictor.cs
 * @author Joshua Starkson
 *
 * @brief	This class is used to track the threat influence of ghosts on the map. For each frame 
 * that a new Influence map is generated from the GameState, the GhostPredictor class,
 * when  the method spreadGhostThreat() is called, the threat influence is propagated through
 * the map along a path that the ghost would take given the position of Ms Pac-man. If the motion
 * of the ghost would be random, the threat is divided by the number of possible directions for
 * the ghost, and then propogated along each possible path.
 */


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;


namespace Pacman.Simulator
{
	public class GhostPredicter
	{
		//GameState set iterativly on calls to spreadGhostThreat, 
		//if objects are passed by reference in c#, 
		//then we should be able to pass the GameState 
		//through the constructor instead.
		GameState gameState;
		
		//The map as it currently stands.
		Map nodes;
		
		//the starting value for the threat influence
		//we could also turn this into a float and refactor to 
		//include a threatRate that acts as the decrimentor
		//for the threat throughout a recursion.
		int initialThreat;
		
		public GhostPredicter(int initT) 
		{ 
			initialThreat = initT;
		}
		
		//this is the main call that perpetraits the ghosts influence onto the influence map
		public int[][] spreadGhostThreat(int[][] map, GameState gs, Map n)
		{
			nodes = n;
			gameState = gs;
			
			//if the ghost is dangerous (in chase-mode and entered into the map), expand its threat along its path given Ms Pacs current position.
			if (gameState.Red.Chasing && gameState.Red.Entered) {
				spreadRedThreat(map, gameState.Red.Node.X, gameState.Red.Node.Y, initialThreat, gameState.Red.Direction, n);
			}
			
			if (gameState.Blue.Chasing && gameState.Blue.Entered) {
				spreadBlueThreat(map, gameState.Blue.Node.X, gameState.Blue.Node.Y, initialThreat, gameState.Blue.Direction, n);
			}
			
			if (gameState.Pink.Chasing && gameState.Pink.Entered) {
				spreadPinkThreat(map, gameState.Pink.Node.X, gameState.Pink.Node.Y, initialThreat, gameState.Pink.Direction, n);
			}
			
			if (gameState.Brown.Chasing && gameState.Brown.Entered) {
				spreadBrownThreat(map, gameState.Brown.Node.X, gameState.Brown.Node.Y, initialThreat, gameState.Brown.Direction, n);
			}
			
			return map;
		}
		
		//a better version would take into account the change in speed when passing through a tunnel.
		public void spreadRedThreat(int[][] iMap, int x, int y, int threat, Direction direction, Map map)
		{
			//if there is no threat return
			if (threat <= 0) {
				return;
			}
			
			//do the alteration to the influence map and post-decrement of the treat value
			iMap[x][y] += threat--;
			
			//calculate the next postition
			int pX, pY;
			Direction newD;
			pX = gameState.Pacman.Node.X;
			pY = gameState.Pacman.Node.Y;
			newD = direction;
			
			//chose next position as if you were the red ghost
			if (Math.Abs(x - pX) > Math.Abs(y - pY)) {
				if (x <= pX ) {
					newD = Direction.Left;
				} else {
					newD = Direction.Right;
				}
				
				if ((newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) || 
					newD == gameState.Red.InverseDirection(direction) || 
					(newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall)) {
					
					if(y <= pY) {
						newD = Direction.Down;
					} else {
						newD = Direction.Up;
					}
                }
						
			} else {
				if (y <= pY) {
					newD = Direction.Down;
				} else {
					newD = Direction.Up;
				}
			
				
				if 	(newD == gameState.Red.InverseDirection(direction) || 
					(newD == Direction.Down && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) || 
					(newD == Direction.Up && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) ) {
					
					if(x <= pX) {
						newD = Direction.Left;
					} else {
						newD = Direction.Right;
					}
				}
			}
			
			if 	(newD == gameState.Red.InverseDirection(direction)) {
				newD = direction;
			}
			
			//if i'm gonna wander into a wall pick valid move from red's favorite directions
			if ( 	(newD == Direction.Left && map.Nodes[(x - 1), y].Type == Node.NodeType.Wall) ||
					(newD == Direction.Right && map.Nodes[(x + 1), y].Type == Node.NodeType.Wall) ||
					(newD == Direction.Up && map.Nodes[x, (y + 1)].Type == Node.NodeType.Wall) ||
					(newD == Direction.Down && map.Nodes[x, (y - 1)].Type == Node.NodeType.Wall)	) {
					
				if (newD != Direction.Down && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) {
					newD = Direction.Up;
				} else if (newD != Direction.Right && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) {
					newD = Direction.Left;
				} else if (newD != Direction.Up && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) {
					newD = Direction.Down;
				} else if (newD != Direction.Left && map.Nodes[(x + 1), y].Type != Node.NodeType.Wall) {
					newD = Direction.Right;
				}
			}
			
			if (newD == Direction.Left) {
				x--;
			} else if (newD == Direction.Right) {
				x++;
			} else if (newD == Direction.Up) {
				y++;
			} else if (newD == Direction.Down) {
				y--;
			}
				
			//propogate the tail-recursive call
			spreadRedThreat(iMap, x, y, threat, newD, map);
		}

		public void spreadBlueThreat(int[][] iMap, int x, int y, int threat, Direction direction, Map map)
		{
			//if there is no threat return
			if (threat <= 0) {
				return;
			}
			
			//do the alteration to the influence map and post-decrement of the treat value
			iMap[x][y] += threat--;
			
			//calculate the next postition
			int pX, pY;
			float d;
            Direction newD;
			pX = gameState.Pacman.Node.X;
			pY = gameState.Pacman.Node.Y;
			newD = direction;
			d = (float)Math.Sqrt(Math.Pow(x - gameState.Red.X, 2) + Math.Pow(x - gameState.Red.Y, 2));
			
			//if the distance is less then 50.0 acts as red with different favorites 
			if (d < 50.0f) {
				//chose next position as if you were the red ghost
				if (Math.Abs(x - pX) > Math.Abs(y - pY)) {
					if (x <= pX ) {
						newD = Direction.Left;
					} else {
						newD = Direction.Right;
					}
					
					if ((newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) || 
						newD == gameState.Red.InverseDirection(direction) || 
						(newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall)) {
						
						if(y <= pY) {
							newD = Direction.Down;
						} else {
							newD = Direction.Up;
						}
					}
				} else {
					if (y <= pY) {
						newD = Direction.Down;
					} else {
						newD = Direction.Up;
					}
				}
				
				if 	(newD == gameState.Red.InverseDirection(direction) || 
					(newD == Direction.Down && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) || 
					(newD == Direction.Up && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) ) {
					
					if(x <= pX) {
						newD = Direction.Left;
					} else {
						newD = Direction.Right;
					}
				}
				
				if 	(newD == gameState.Red.InverseDirection(direction) ){
					newD = direction;
				}
				
				//if i'm gonna wander into a wall pick valid move from blue's favorite directions
				if ( 	(newD == Direction.Left && map.Nodes[(x - 1), y].Type == Node.NodeType.Wall) ||
						(newD == Direction.Right && map.Nodes[(x + 1), y].Type == Node.NodeType.Wall) ||
						(newD == Direction.Up && map.Nodes[x, (y + 1)].Type == Node.NodeType.Wall) ||
						(newD == Direction.Down && map.Nodes[x, (y - 1)].Type == Node.NodeType.Wall)	) {
					
					if (newD != Direction.Left && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) {
						newD = Direction.Right;
					} else if (newD != Direction.Right && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) {
						newD = Direction.Left;
					} else if (newD != Direction.Up && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) {
						newD = Direction.Down;
					} else if (newD != Direction.Down && map.Nodes[(x + 1), y].Type != Node.NodeType.Wall) {
						newD = Direction.Up;
					}
				}
				
			} else {
				// Blue otherwise always tries to minimize x till it is zero, the it moves in
				
				// minimize X
				if( Math.Abs(x - pX) != 0 ) {
					if( x >= pX) {
						newD = Direction.Right;
					} else {
						newD = Direction.Left;
					}
					
					if	(newD == gameState.Red.InverseDirection(direction) ||
						(newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) ||  
						(newD == Direction.Left && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall)) {
						
						if( y >= pY ) {
							newD = Direction.Up;
						} else {
							newD = Direction.Down;
						}
					}
					
				} else {
					if( y <= pY ) {
						newD = Direction.Down;
					} else {
						newD = Direction.Up;
					}
					
					if	(newD == gameState.Red.InverseDirection(direction) || 
						(newD == Direction.Down && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) || 
						(newD == Direction.Up && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) ) {
						
						if( x <= pX ) {
							newD = Direction.Left;
						} else {
							newD = Direction.Right;
						}
					}
				}
				
				if 	(newD == gameState.Red.InverseDirection(direction) ){
					newD = direction;
				}
				
				//if i'm gonna wander into a wall pick valid move from blue's favorite directions
				if ( 	(newD == Direction.Left && map.Nodes[(x - 1), y].Type == Node.NodeType.Wall) ||
						(newD == Direction.Right && map.Nodes[(x + 1), y].Type == Node.NodeType.Wall) ||
						(newD == Direction.Up && map.Nodes[x, (y + 1)].Type == Node.NodeType.Wall) ||
						(newD == Direction.Down && map.Nodes[x, (y - 1)].Type == Node.NodeType.Wall)	) {
					
					if (newD != Direction.Left && map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall) {
						newD = Direction.Right;
					} else if (newD != Direction.Right && map.Nodes[(x - 1), y].Type != Node.NodeType.Wall) {
						newD = Direction.Left;
					} else if (newD != Direction.Up && map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall) {
						newD = Direction.Down;
					} else if (newD != Direction.Down && map.Nodes[(x + 1), y].Type != Node.NodeType.Wall) {
						newD = Direction.Up;
					}
				}
			}
			
			if (newD == Direction.Left) {
				x--;
			} else if (newD == Direction.Right) {
				x++;
			} else if (newD == Direction.Up) {
				y++;
			} else if (newD == Direction.Down) {
				y--;
			}
				
			//propogate the tail-recursive call
			spreadBlueThreat(iMap, x, y, threat, newD, map);		
		}
		
		public void spreadPinkThreat(int[][] iMap, int x, int y, int threat, Direction direction, Map map)
		{
			//if there is no threat return
			if (threat <= 0) {
				return;
			}
			
			//calculate the next postition
			int pX, pY;
			float d;
            Direction newD;
			pX = gameState.Pacman.Node.X;
			pY = gameState.Pacman.Node.Y;
			newD = direction;
			d = (float)Math.Sqrt(Math.Pow(x - pX, 2) + Math.Pow(y - pY, 2));
			
			if (d > 30.0f && GameState.Random.Next(0, 5) == 0) {
			//move randomly (30.0f == randomMoveDistance, and 5 == randomMove)
				
				//do the alteration to the influence map and post-decrement of the treat value
				iMap[x][y] += threat--;
				
				int count = 0;
				//count all directions that dont lead to walls and that are not the inverse of the direction
				if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
					count++;
				} 
					
				if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
					count++;
				} 
					
				if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
					count++;
				} 
					
				if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
					count++;
				}
				
				//divide the threat by the number of directions.
				if (count > 1) {
					threat = threat / count;
				}
				
				if (threat < 1) {
					threat = 1;
				}
				
				//recurse in all directions
				if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
					spreadPinkThreat(iMap, (x + 1), y, threat, Direction.Right, map);
				} 
				
				if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
					spreadPinkThreat(iMap, (x - 1), y, threat, Direction.Left, map);
				} 
				
				if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
					spreadPinkThreat(iMap, x, (y + 1), threat, Direction.Up, map);
				} 
				
				if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
					spreadPinkThreat(iMap, x, (y - 1), threat, Direction.Down, map);
				} 
			} else {
				
				if ( d > 120 || gameState.Pacman.Direction == Direction.None ) {
				//move as red with red favorites by switching to the spreadRedThreat call
					spreadRedThreat(iMap, x, y, threat, direction, map);
				} else {
				//do Pink motion
					//do the alteration to the influence map and post-decrement of the treat value
					iMap[x][y] += threat--;
					
					Direction pDir = gameState.Pacman.Direction;
					
					switch( pDir ) {
						case Direction.Up:
							if( y >= pY ) {
								if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
									newD = Direction.Down;
								}
								if( x <= pX ) {
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
								} else {
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
								}
								if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
									newD = Direction.Up;
								}
							} else {
								if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
									newD = Direction.Up;
								}
								if( x <= pX ) {
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
								} else {
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
								}
								if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
									newD = Direction.Down;
								}
							}
							break;
						case Direction.Down:
							if( y <= pY ) {
								if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
									newD = Direction.Up;
								}
								if( x <= pX ) {
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
								} else {
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
								}
								if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
									newD = Direction.Down;
								}
							} else {
								if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
									newD = Direction.Down;
								}
								if( x <= pX ) {
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
								} else {
									if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
										newD = Direction.Left;
									}
									if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
										newD = Direction.Right;
									}
								}
								if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
									newD = Direction.Up;
								}
							}
							break;
						case Direction.Left:
							if( x <= pX ) {
								if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
									newD = Direction.Right;
								}
								if( y <= pY ) {
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
								} else {
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
								}
								if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
									newD = Direction.Left;
								}
							} else {
								if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
									newD = Direction.Left;
								}
								if( y <= pY ) {
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
								} else {
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
								}
								if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
									newD = Direction.Right;
								}
							}
							break;
						case Direction.Right:
							if( x >= pX ) {
								if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
									newD = Direction.Left;
								}
								if( y <= pY ) {
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
								} else {
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
								}
								if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
									newD = Direction.Right;
								}
							} else {
								if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
									newD = Direction.Right;
								}
								if(  y <= pY ) {
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
								} else {
									if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
										newD = Direction.Down;
									}
									if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
										newD = Direction.Up;
									}
								}
								if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
									newD = Direction.Left;
								}
							}
							break;
					}
					
					switch (newD){
						case Direction.Up:
							y++;
							break;
						case Direction.Right:
							x++;
							break;
						case Direction.Down:
							y--;
							break;
						case Direction.Left:
							x--;
							break;
					}
					
					spreadPinkThreat(iMap, x, y, threat, newD, map);
				}
			}
		}
		
		public void spreadBrownThreat(int[][] iMap, int x, int y, int threat, Direction direction, Map map)
		{
			//move dividing threat by the number of directions but having a call traveling down all paths.
			if (threat <= 0) {
				return;
			}

            //do the alteration to the influence map and post-decrement of the treat value
            iMap[x][y] += threat--;
			
			int count = 0;
			
			if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
				count++;
			} 
				
			if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
				count++;
			} 
				
			if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
				count++;
			} 
				
			if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
				count++;
			}
			
			//divide the threat by the number of directions
			if (count > 1) {
				threat = threat / count;
			}
			if (threat < 1) {
				threat = 1;
			}
			
			if(map.Nodes[(x + 1), y].Type != Node.NodeType.Wall && direction != Direction.Left) {
				spreadBrownThreat(iMap, (x + 1), y, threat, Direction.Right, map);
			} 
				
			if(map.Nodes[(x - 1), y].Type != Node.NodeType.Wall && direction != Direction.Right) {
				spreadBrownThreat(iMap, (x - 1), y, threat, Direction.Left, map);
			} 
				
			if(map.Nodes[x, (y + 1)].Type != Node.NodeType.Wall && direction != Direction.Down) {
				spreadBrownThreat(iMap, x, (y + 1), threat, Direction.Up, map);
			} 
				
			if(map.Nodes[x, (y - 1)].Type != Node.NodeType.Wall && direction != Direction.Up) {
				spreadBrownThreat(iMap, x, (y - 1), threat, Direction.Down, map);
			} 
			
		}
	}
}